﻿using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Colors;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.GraphicsInterface;
using Autodesk.AutoCAD.Runtime;
using System;
using System.Collections;
using System.Collections.Generic;

using System.IO;
using System.Linq;

using AcadApplication = Autodesk.AutoCAD.ApplicationServices.Application;
using Polyline = Autodesk.AutoCAD.DatabaseServices.Polyline;


namespace imvs
{
    public class ImvsApplication : Autodesk.AutoCAD.Runtime.IExtensionApplication
    {
        static Point3d ZPOINT = new Point3d(0, 0, 1);
        public void Initialize()
        {
        }
        public void Terminate()
        {
        }

        /// <summary>
        /// 带误差范围的浮点数相等比较
        /// </summary>
        /// <param name="a">第一个浮点数</param>
        /// <param name="b">第二个浮点数</param>
        /// <param name="epsilon">允许的误差范围</param>
        /// <returns>当差值小于epsilon时返回true</returns>
        public static bool FloatEqual(double a, double b, double epsilon)
        {
            return Math.Abs(a - b) < epsilon;
        }

        // 16进制字符串转换为handle
        public static Handle TryParseHandle(string hexString)
        {
            long handleValue = Convert.ToInt64(hexString.Replace("0x", ""), 16);
            return new Handle(handleValue);
        }

        // handle转换为16进制字符串
        public static string HandleToHexString(Handle handle)
        {
            return handle.Value.ToString("X"); ;
        }

        // 将16进制字符串Handle转为ObjectId
        public static ObjectId HandleToObjectId(Database db, string handleStr)
        {
            Handle handle = TryParseHandle(handleStr);
            ObjectId id = ObjectId.Null;
            db.TryGetObjectId(handle, out id);
            return id;
        }


        public static Tuple<bool, double, double> MvsDealLine(ObjectId objId, double epsilon, string MVS_APP_POINT_DICT, ObjectId circleObjId, Transaction tr)
        {
            Document doc = AcadApplication.DocumentManager.MdiActiveDocument;

            var ent = (Line)tr.GetObject(objId, OpenMode.ForWrite);
            var spt = ent.StartPoint;
            var ept = ent.EndPoint;

            // 坐标转换为字符串
            var str_spt = StringUtils.PointToString(spt);
            var str_ept = StringUtils.PointToString(ept);
            //EditorUtils.Print(str_spt);
            //EditorUtils.Print(str_ept);

            //当前的z(初始值为起点的z值)
            var mvs_last_z = spt.Z;
            double sz = 0;
            //查找起点的新z坐标
            var o = DictUtils.GetDictData(MVS_APP_POINT_DICT, str_spt, tr);
            if (o != null)
            {
                sz = (double)o;
                mvs_last_z = sz;
            }
            // 移动圆心到pt(高亮提示: 一个半径为50, 颜色为红色的圆)
            EntityUtils.SetCircleProperties(circleObjId, spt, 50, tr);
            // 交互获取新的z值
            var res = EditorUtils.GetDouble("\n请指定起点的Z值:", mvs_last_z);
            if (!res.Item1)
            {
                return Tuple.Create(false, 0.0, 0.0);
            }
            sz = res.Item2;

            EditorUtils.Print(new ArrayList() { str_spt, sz });
            //记实到词典
            DictUtils.SetDictData(MVS_APP_POINT_DICT, str_spt, sz, tr);
            // 更新当前的默认z
            if (!FloatEqual(sz, mvs_last_z, epsilon))
            {
                mvs_last_z = sz;
            }

            double tz = 0;
            //查找终点的新z坐标
            o = DictUtils.GetDictData(MVS_APP_POINT_DICT, str_ept, tr);
            if (o != null)
            {
                tz = (double)o;
                mvs_last_z = tz;
            }
            // 移动圆心到pt(高亮提示: 一个半径为50, 颜色为红色的圆)
            EntityUtils.SetCircleProperties(circleObjId, ept, 50, tr);
            // 交互获取新的z值
            res = EditorUtils.GetDouble("\n请指定终点的Z值:", mvs_last_z);
            if (!res.Item1)
            {
                return Tuple.Create(false, 0.0, 0.0);
            }
            tz = res.Item2;

            //记实到词典
            DictUtils.SetDictData(MVS_APP_POINT_DICT, str_ept, tz, tr);
            // 更新当前的默认z
            if (!FloatEqual(tz, mvs_last_z, epsilon))
            {
                mvs_last_z = tz;
            }

            return Tuple.Create(true, sz, tz);
        }

        // 写入文件行（使用AutoCAD环境路径）
        public static bool MvsFileWriteLine(IEnumerable<object> myList, StreamWriter writer, string separator)
        {
            try
            {
                //using (var writer = new StreamWriter(filePath, true))
                //{
                bool first = true;
                foreach (var item in myList)
                {
                    if (!first) writer.Write(separator);
                    writer.Write(item?.ToString() ?? "");
                    first = false;
                }
                writer.WriteLine();
                //}
                return true;
            }
            catch (System.Exception ex)
            {

                Editor editor = AcadApplication.DocumentManager.MdiActiveDocument.Editor;
                editor.WriteMessage($"写入文件错误: {ex.Message}\n");
                return false;
            }
        }

        public static void MvsFlushToFile(StreamWriter writer, string MVS_APP_POINT_DICT, string MVS_APP_ENTITY_DICT, Transaction tr)
        {
            var dict = DictUtils.ListDictDatas(MVS_APP_ENTITY_DICT, tr);
            var doc = Application.DocumentManager.MdiActiveDocument;
            int count = 0;
            // 遍历键
            foreach (string handle in dict.Keys)
            {
                ObjectId objdId = HandleToObjectId(doc.Database, handle);
                if (objdId.IsValid)
                {
                    var obj = tr.GetObject(objdId, OpenMode.ForRead);
                    if (obj != null && obj is Line ent)
                    {
                        var spt = ent.StartPoint;
                        var ept = ent.EndPoint;
                        var str_spt = StringUtils.PointToString(spt);
                        var str_ept = StringUtils.PointToString(ept);

                        //查找起点和终点的新z坐标                        
                        var o1 = DictUtils.GetDictData(MVS_APP_POINT_DICT, str_spt, tr);
                        var o2 = DictUtils.GetDictData(MVS_APP_POINT_DICT, str_ept, tr);
                        if (o1 != null && o2 != null)
                        {
                            var sz = (double)o1;
                            var tz = (double)o2;

                            //计数器 + 1
                            count += 1;

                            EditorUtils.Print(new ArrayList() { "\n[", count, "] ", "句柄:", handle, "\t起点:", str_spt, " -> ", sz, " \t终点: ",
                    str_ept, " -> ", tz});

                            // 批量写入到数据文件
                            MvsFileWriteLine(new ArrayList()
                            {
                                handle, spt.X, spt.Y, spt.Z, ept.X, ept.Y, ept.Z, sz, tz
                            }.Cast<object>(), writer, "\t");
                        }
                    }
                }
            }
            EditorUtils.Print("\n------  写入完毕 ------ ");

        }

        public static ObjectId GetOrCreateCircleOnLayer(string layerName, Transaction tr)
        {
            var objId = EntityUtils.GetEntityOnLayer("CIRCLE", layerName);
            if (objId.IsNull)
            {
                // 创建一个新的圆
                return EntityUtils.CreateCircle(Point3d.Origin, 1, layerName, tr);
            }
            else
            {
                return objId;
            }
        }

        public static ObjectId GetOrCreateLineOnLayer(string layerName, Transaction tr)
        {
            var objId = EntityUtils.GetEntityOnLayer("LINE", layerName);
            if (objId.IsNull)
            {
                // 创建一个起点(0,0,0), 终点(0,0,1)的直线
                return EntityUtils.CreateLine(Point3d.Origin, ZPOINT, layerName, tr);
            }
            else
            {
                return objId;
            }
        }

        public static void SaveLineToDict(string handle, Point3d spt, Point3d ept, double sz, double tz, string MVS_APP_POINT_DICT, string MVS_APP_ENTITY_DICT, Transaction tr)
        {
            DictUtils.SetDictData(MVS_APP_ENTITY_DICT, handle, handle, tr);
            DictUtils.SetDictData(MVS_APP_POINT_DICT, StringUtils.PointToString(spt), sz, tr);
            DictUtils.SetDictData(MVS_APP_POINT_DICT, StringUtils.PointToString(ept), tz, tr);
        }

        public static void DeleteLineFromDict(string handle, Point3d spt, Point3d ept, double sz, double tz, string MVS_APP_POINT_DICT, string MVS_APP_ENTITY_DICT, Transaction tr)
        {
            DictUtils.RemoveDictData(MVS_APP_ENTITY_DICT, handle, tr);
            DictUtils.RemoveDictData(MVS_APP_POINT_DICT, StringUtils.PointToString(spt), tr);
            DictUtils.RemoveDictData(MVS_APP_POINT_DICT, StringUtils.PointToString(ept), tr);
        }

        public static void CleanMvsAppDict(string MVS_APP_POINT_DICT, string MVS_APP_ENTITY_DICT, Transaction tr)
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;

            var handleList = DictUtils.ListDictDatas(MVS_APP_ENTITY_DICT, tr);
            var pointList = DictUtils.ListDictDatas(MVS_APP_POINT_DICT, tr);
            var handle_list_to_clean = new List<string>();
            var valid_point_dict = new Dictionary<string, double>();
            // 遍历键
            foreach (string handle in handleList.Keys)
            {
                var objdId = HandleToObjectId(doc.Database, handle);
                if (objdId.IsValid)
                {
                    var o = tr.GetObject(objdId, OpenMode.ForRead);
                    if (o != null && o is Entity ent)
                    {
                        var line = (Line)ent;
                        var spt = line.StartPoint;
                        var ept = line.EndPoint;
                        var str_spt = StringUtils.PointToString(spt);
                        var str_ept = StringUtils.PointToString(ept);
                        var dobj1 = DictUtils.GetDictData(MVS_APP_POINT_DICT, str_spt, tr);
                        var dobj2 = DictUtils.GetDictData(MVS_APP_POINT_DICT, str_ept, tr);
                        if (dobj1 != null)
                        {
                            var sz = (double)dobj1;
                            //valid_point_dict.Add(str_spt, sz);
                            valid_point_dict[str_spt] = valid_point_dict.ContainsKey(str_spt) ? valid_point_dict[str_spt] : sz;
                        }
                        if (dobj2 != null)
                        {
                            var tz = (double)dobj2;
                            //valid_point_dict.Add(str_ept, tz);
                            valid_point_dict[str_ept] = valid_point_dict.ContainsKey(str_ept) ? valid_point_dict[str_ept] : tz;
                        }
                        if (dobj1 == null && dobj2 == null)
                        {
                            handle_list_to_clean.Add(handle);
                        }
                    }
                    else
                    {
                        handle_list_to_clean.Add(handle);
                    }
                }
                else
                {
                    handle_list_to_clean.Add(handle);
                }
            }

            //重新创建point词典(排除非法的词典)
            DictUtils.RemoveDict(MVS_APP_POINT_DICT, tr);
            DictUtils.CreateDict(MVS_APP_POINT_DICT, tr);
            foreach (KeyValuePair<string, double> kvp in valid_point_dict)
            {
                DictUtils.SetDictData(MVS_APP_POINT_DICT, kvp.Key, kvp.Value, tr);
            }
            // 清理handle
            foreach (string handle in handle_list_to_clean)
            {
                DictUtils.RemoveDictData(MVS_APP_ENTITY_DICT, handle, tr);
            }
        }

        [CommandMethod("mark-zline")]
        public static void MarkZLine()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;
            Database db = doc.Database;

            //保存当前线宽可视化设置
            var old_lwd = Application.GetSystemVariable("LWDISPLAY");
            var old_cmdecho = Application.GetSystemVariable("CMDECHO");

            //显示线宽
            Application.SetSystemVariable("LWDISPLAY", 1);
            //关闭命令回显
            Application.SetSystemVariable("CMDECHO", 0);
            // 创建全局词典
            var MVS_APP_POINT_DICT = "IMVS_APP_POINT";
            var MVS_APP_ENTITY_DICT = "IMVS_APP_ENTITY";
            var MVS_APP_INVISIBLE_LAYER = "IMVS_APP_INVISIBLE_LAYER1";
            // 交互高亮提示用的圆
            ObjectId circleObjId = ObjectId.Null;

            // 初始化操作
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                // 数据库操作代码
                DictUtils.CreateDict(MVS_APP_POINT_DICT, tr);
                DictUtils.CreateDict(MVS_APP_ENTITY_DICT, tr);

                //图层颜色为红色, 线宽为0.70mm
                LayerUtils.CreateLayer(MVS_APP_INVISIBLE_LAYER, 1, LineWeight.LineWeight070, false, false, false, tr);
                //获取或创建圆
                circleObjId = GetOrCreateCircleOnLayer(MVS_APP_INVISIBLE_LAYER, tr);
                // 创建一个新的圆
                // 提交事务
                tr.Commit();
            }

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                try
                {
                    //精度-- 用于比较浮点数是否相等
                    double epsilon = 0.000001;

                    //计数
                    int count = 0;

                    while (true)
                    {
                        ObjectId lineObjId = EditorUtils.SelectEntity("\n请选择一条直线:");
                        if (lineObjId.IsNull) break;

                        var lineObj = tr.GetObject(lineObjId, OpenMode.ForRead);
                        if (lineObj == null) break;

                        if (lineObj is Line line)
                        {
                            // 处理直线并返回交互得到的2个z值
                            Tuple<bool, double, double> res = MvsDealLine(lineObjId, epsilon, MVS_APP_POINT_DICT, circleObjId, tr);
                            if (!res.Item1)
                            {
                                EditorUtils.Print("\n用户取消当前操作，程序忽略当前选择的直线!");
                                break;
                            }

                            // 计数器+1
                            count += 1;

                            // 新的起点z值
                            double sz = res.Item2;
                            // 新的终点z值
                            double tz = res.Item3;
                            // 记录到词典
                            var spt = line.StartPoint;
                            var ept = line.EndPoint;
                            string handle = HandleToHexString(line.Handle);
                            DictUtils.SetDictData(MVS_APP_ENTITY_DICT, handle, handle, tr);
                            // HACK 隐藏圆
                            EntityUtils.SetCircleProperties(circleObjId, Point3d.Origin, 1, tr);
                            // 打印消息
                            EditorUtils.Print(new ArrayList() {
                                 "\n指定[", count, "] ", "句柄:", handle, "\t起点:", spt, " -> ", sz, " \t终点: ", ept,
                  " -> ", tz
                            });
                        }
                    }

                    // 刷新显示
                    //doc.SendStringToExecute("._REGENALL ", false, false, false);
                    //doc.Editor.Regen();

                    // 统计词典中已处理的图元个数
                    var totalCount = DictUtils.CountDictDatas(MVS_APP_ENTITY_DICT, tr);
                    EditorUtils.Print(new ArrayList()
                    {
                        "处理前: ", totalCount- count, "  处理后: ", totalCount, "  新标记:", count
                    });

                    // 提交事务
                    if (count > 0) { tr.Commit(); }

                }
                catch (Autodesk.AutoCAD.Runtime.Exception ex)
                {
                    // 关键步骤：取消时回滚事务
                    tr.Abort();
                    // 打印错误信息
                    ed.WriteMessage(ex.Message);
                    ed.WriteMessage("\n");
                }
            }

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                if (circleObjId.IsValid)
                {
                    EntityUtils.DeleteEntity(circleObjId, tr);
                }
                tr.Commit();
            }

            // 恢复线宽设置
            Application.SetSystemVariable("LWDISPLAY", old_lwd);
            Application.SetSystemVariable("CMDECHO", old_cmdecho);
        }

        [CommandMethod("draw-zline")]
        public static void DrawZLine()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;
            Database db = doc.Database;

            //保存当前线宽可视化设置
            var old_lwd = Application.GetSystemVariable("LWDISPLAY");
            var old_cmdecho = Application.GetSystemVariable("CMDECHO");

            //显示线宽
            Application.SetSystemVariable("LWDISPLAY", 1);
            //关闭命令回显
            Application.SetSystemVariable("CMDECHO", 0);
            // 创建全局词典
            var MVS_APP_POINT_DICT = "IMVS_APP_POINT";
            var MVS_APP_ENTITY_DICT = "IMVS_APP_ENTITY";
            var MVS_APP_INVISIBLE_LAYER = "IMVS_APP_INVISIBLE_LAYER3";
            // 交互高亮提示用的直线
            ObjectId lineObjId = ObjectId.Null;

            // 初始化操作
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                // 数据库操作代码
                DictUtils.CreateDict(MVS_APP_POINT_DICT, tr);
                DictUtils.CreateDict(MVS_APP_ENTITY_DICT, tr);

                //图层颜色为红色, 线宽为0.70mm
                LayerUtils.CreateLayer(MVS_APP_INVISIBLE_LAYER, 1, LineWeight.LineWeight070, false, false, false, tr);
                //获取或创建直线
                lineObjId = GetOrCreateLineOnLayer(MVS_APP_INVISIBLE_LAYER, tr);
                // 提交事务
                tr.Commit();
            }

            var ret = EditorUtils.GetPoint("\n请指定起点: ");
            if (ret.Item1)
            {
                Point3d p1 = ret.Item2;

                var ret2 = EditorUtils.GetDouble("\n请输入z值: ");
                if (ret2.Item1)
                {
                    double z1 = ret2.Item2;

                    using (Transaction tr = db.TransactionManager.StartTransaction())
                    {
                        // 当前图层名称
                        string clayerName = LayerUtils.GetLayerName(db.Clayer, tr);

                        //计数
                        int count = 0;

                        while (true)
                        {
                            ret = EditorUtils.GetPoint("\n指定下一点: ", p1);
                            if (!ret.Item1)
                            {
                                EditorUtils.Print("\n结束绘制。");
                                break;
                            }
                            Point3d p2 = ret.Item2;

                            EntityUtils.SetLinePoints(lineObjId, new Point3d(p1.X, p1.Y, z1), new Point3d(p2.X, p2.Y, z1), tr);

                            ret2 = EditorUtils.GetDouble("\n请输入z值: ", z1);
                            if (!ret2.Item1)
                            {
                                EditorUtils.Print("\n无效的起点z值，操作取消。");
                                break;
                            }

                            double z2 = ret2.Item2;
                            // 隐藏直线
                            EntityUtils.SetLinePoints(lineObjId, Point3d.Origin, ZPOINT, tr);
                            // 绘制当前线段
                            ObjectId objId = EntityUtils.CreateLine(new Point3d(p1.X, p1.Y, z1), new Point3d(p2.X, p2.Y, z2), clayerName, tr);
                            // 记录到词典
                            DBObject obj = tr.GetObject(objId, OpenMode.ForRead);
                            if (obj != null && obj is Line line)
                            {
                                string handle = HandleToHexString(line.Handle);
                                SaveLineToDict(handle, line.StartPoint, line.EndPoint, z1, z2, MVS_APP_POINT_DICT, MVS_APP_ENTITY_DICT, tr);
                            }
                            // 更新循环
                            p1 = p2;
                            z1 = z2;

                            // 计数器+1
                            count += 1;
                        }

                        if (count > 0)
                        {
                            // 提交事务
                            tr.Commit();
                        }

                    }
                }
            }

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                if (lineObjId.IsValid)
                {
                    EntityUtils.DeleteEntity(lineObjId, tr);
                }
                tr.Commit();
            }
            // 恢复线宽设置
            Application.SetSystemVariable("LWDISPLAY", old_lwd);
            Application.SetSystemVariable("CMDECHO", old_cmdecho);
        }

        [CommandMethod("change-zline")]
        public static void ChangeZLine()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;
            Database db = doc.Database;

            //保存当前线宽可视化设置
            var old_lwd = Application.GetSystemVariable("LWDISPLAY");
            var old_cmdecho = Application.GetSystemVariable("CMDECHO");

            //显示线宽
            Application.SetSystemVariable("LWDISPLAY", 1);
            //关闭命令回显
            Application.SetSystemVariable("CMDECHO", 0);
            // 创建全局词典
            var MVS_APP_POINT_DICT = "IMVS_APP_POINT";
            var MVS_APP_ENTITY_DICT = "IMVS_APP_ENTITY";
            var MVS_APP_INVISIBLE_LAYER = "IMVS_APP_INVISIBLE_LAYER4";
            // 交互高亮提示用的圆
            ObjectId circleObjId = ObjectId.Null;

            // 初始化操作
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                // 数据库操作代码
                DictUtils.CreateDict(MVS_APP_POINT_DICT, tr);
                DictUtils.CreateDict(MVS_APP_ENTITY_DICT, tr);

                //图层颜色为红色, 线宽为0.70mm
                LayerUtils.CreateLayer(MVS_APP_INVISIBLE_LAYER, 1, LineWeight.LineWeight070, false, false, false, tr);
                //获取或创建圆
                circleObjId = GetOrCreateCircleOnLayer(MVS_APP_INVISIBLE_LAYER, tr);
                // 提交事务
                tr.Commit();
            }

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                try
                {
                    //精度-- 用于比较浮点数是否相等
                    double epsilon = 0.000001;

                    //计数
                    int count = 0;

                    while (true)
                    {
                        ObjectId lineObjId = EditorUtils.SelectEntity("\n请选择一条直线:");
                        if (lineObjId.IsNull) break;

                        var lineObj = tr.GetObject(lineObjId, OpenMode.ForWrite);
                        if (lineObj == null) break;

                        if (lineObj is Line line)
                        {
                            // 处理直线并返回交互得到的2个z值
                            Tuple<bool, double, double> res = MvsDealLine(lineObjId, epsilon, MVS_APP_POINT_DICT, circleObjId, tr);
                            if (!res.Item1)
                            {
                                EditorUtils.Print("\n用户取消当前操作，程序忽略当前选择的直线!");
                                break;
                            }

                            // 计数器+1
                            count += 1;

                            // 新的起点z值
                            double sz = res.Item2;
                            // 新的终点z值
                            double tz = res.Item3;
                            // 记录到词典
                            var spt = line.StartPoint;
                            var ept = line.EndPoint;
                            string handle = HandleToHexString(line.Handle);
                            // HACK 隐藏圆
                            EntityUtils.SetCircleProperties(circleObjId, Point3d.Origin, 1, tr);
                            // 修改直线的z值
                            Point3d newSpt = new Point3d(spt.X, spt.Y, sz);
                            Point3d newEpt = new Point3d(ept.X, ept.Y, tz);
                            EntityUtils.SetLinePoints(lineObjId, newSpt, newEpt, tr);
                            // 更新词典记录
                            SaveLineToDict(handle, newSpt, newEpt, sz, tz, MVS_APP_POINT_DICT, MVS_APP_ENTITY_DICT, tr);
                            // 打印消息
                            EditorUtils.Print(new ArrayList() {
                                 "\n指定[", count, "] ", "句柄:", handle, "\t起点:", StringUtils.PointToString(newSpt), " -> ", sz, " \t终点: ", StringUtils.PointToString(newEpt),
                  " -> ", tz
                            });
                        }
                    }

                    //清理词典冗余数据
                    CleanMvsAppDict(MVS_APP_POINT_DICT, MVS_APP_ENTITY_DICT, tr);

                    // 提交事务
                    if (count > 0) { tr.Commit(); }

                }
                catch (Autodesk.AutoCAD.Runtime.Exception ex)
                {
                    // 关键步骤：取消时回滚事务
                    tr.Abort();
                    // 打印错误信息
                    ed.WriteMessage(ex.Message);
                    ed.WriteMessage("\n");
                }
            }


            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                if (circleObjId.IsValid)
                {
                    EntityUtils.DeleteEntity(circleObjId, tr);
                }
                tr.Commit();
            }
            // 恢复线宽设置
            Application.SetSystemVariable("LWDISPLAY", old_lwd);
            Application.SetSystemVariable("CMDECHO", old_cmdecho);
        }

        [CommandMethod("count-zline")]
        public static void CountZLine()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;
            Database db = doc.Database;


            // 创建全局词典
            var MVS_APP_POINT_DICT = "IMVS_APP_POINT";
            var MVS_APP_ENTITY_DICT = "IMVS_APP_ENTITY";

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                try
                {
                    var handleList = DictUtils.ListDictDatas(MVS_APP_ENTITY_DICT, tr);

                    //计数
                    int count = 0;

                    foreach (string handle in handleList.Keys)
                    {
                        count += 1;
                        var objdId = HandleToObjectId(db, handle);
                        if (objdId.IsValid)
                        {
                            var o = tr.GetObject(objdId, OpenMode.ForRead);
                            if (o != null && o is Line line)
                            {
                                var spt = line.StartPoint;
                                var ept = line.EndPoint;
                                var str_spt = StringUtils.PointToString(spt);
                                var str_ept = StringUtils.PointToString(ept);
                                var dobj1 = DictUtils.GetDictData(MVS_APP_POINT_DICT, str_spt, tr);
                                var dobj2 = DictUtils.GetDictData(MVS_APP_POINT_DICT, str_ept, tr);
                                if (dobj1 != null && dobj2 != null)
                                {
                                    var sz = (double)dobj1;
                                    var tz = (double)dobj2;
                                    // 打印消息
                                    EditorUtils.Print(new ArrayList() {
                                 "\n指定[", count, "] ", "句柄:", handle, "\t起点:", str_spt, " -> ", sz, " \t终点: ", str_ept,
                  " -> ", tz
                            });
                                }
                            }
                        }
                    }
                }
                catch (Autodesk.AutoCAD.Runtime.Exception ex)
                {
                    // 关键步骤：取消时回滚事务
                    tr.Abort();
                    // 打印错误信息
                    ed.WriteMessage(ex.Message);
                    ed.WriteMessage("\n");
                }
            }
        }

        [CommandMethod("write-zfile")]
        public static void WriteZFile()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;
            Database db = doc.Database;

            // 创建全局词典
            var MVS_APP_CONFIG_DICT = "IMVS_APP_CONFIG";
            var MVS_APP_POINT_DICT = "IMVS_APP_POINT";
            var MVS_APP_ENTITY_DICT = "IMVS_APP_ENTITY";
            var MVS_LAST_DATA_FILE_KEY = "last_data_file";

            // 初始化操作
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                // 数据库操作代码
                DictUtils.CreateDict(MVS_APP_CONFIG_DICT, tr);
                DictUtils.CreateDict(MVS_APP_POINT_DICT, tr);
                DictUtils.CreateDict(MVS_APP_ENTITY_DICT, tr);

                //统计词典中已处理的图元个数
                int totalCount = DictUtils.CountDictDatas(MVS_APP_ENTITY_DICT, tr);
                if (totalCount > 0)
                {
                    EditorUtils.Print("\n======= 批量写入到数据文件  ======");
                    //读取上次记录的数据文件
                    string mvsDataFile = (string)DictUtils.GetDictData(MVS_APP_CONFIG_DICT, MVS_LAST_DATA_FILE_KEY, tr);
                    // 交互选择文件
                    string filename = EditorUtils.GetFileForSave(mvsDataFile);
                    //记录用户选择的数据文件
                    DictUtils.SetDictData(MVS_APP_CONFIG_DICT, MVS_LAST_DATA_FILE_KEY, filename, tr);
                    EditorUtils.Print(new ArrayList() { "\n======= 数据文件存储路径: ", filename, " ======" });
                    // 
                    try
                    {
                        using (StreamWriter writer = new StreamWriter(filename))
                        {
                            MvsFlushToFile(writer, MVS_APP_POINT_DICT, MVS_APP_ENTITY_DICT, tr);
                            EditorUtils.Print("\n======= 写入文件成功  ======");
                        }
                    }
                    catch (IOException ex)
                    {
                        EditorUtils.Print(new ArrayList() { "\n======= 写入文件失败:", ex.Message, "======" });
                    }
                }
                // 提交事务
                tr.Commit();
            }
        }


        [CommandMethod("read-zfile")]
        public static void ReadZFile()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;
            Database db = doc.Database;

            // 创建全局词典
            var MVS_APP_POINT_DICT = "IMVS_APP_POINT";
            var MVS_APP_ENTITY_DICT = "IMVS_APP_ENTITY";

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                // 数据库操作代码
                DictUtils.CreateDict(MVS_APP_POINT_DICT, tr);
                DictUtils.CreateDict(MVS_APP_ENTITY_DICT, tr);

                //清理词典, 移除已被删除的直线
                CleanMvsAppDict(MVS_APP_POINT_DICT, MVS_APP_ENTITY_DICT, tr);

                tr.Commit();
            }


            // 初始化操作
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                // 当前图层名称
                string clayerName = LayerUtils.GetLayerName(db.Clayer, tr);

                // 记录被跳过的行数据
                List<string> ignoredLineDatas = new List<string>();
                int count = 0; // 实际的修改或创建编号
                int lineNumber = 0; // 文件行号

                // 交互选择文件
                string filename = EditorUtils.GetFileForOpen("mvs_data.txt");
                if (filename != null)
                {
                    try
                    {
                        using (StreamReader reader = new StreamReader(filename))
                        {
                            while (!reader.EndOfStream)
                            {
                                string lineData = reader.ReadLine();
                                if (string.IsNullOrEmpty(lineData)) continue;

                                lineNumber += 1;
                                // 拆分字符串
                                var dataList = StringUtils.StringSplit(lineData, "\t");
                                // 依次取出数据
                                string handle = dataList[0];
                                double sx = double.Parse(dataList[1]);
                                double sy = double.Parse(dataList[2]);
                                double sz = double.Parse(dataList[3]);
                                double tx = double.Parse(dataList[4]);
                                double ty = double.Parse(dataList[5]);
                                double tz = double.Parse(dataList[6]);
                                double new_sz = double.Parse(dataList[7]);
                                double new_tz = double.Parse(dataList[8]);

                                var o = DictUtils.GetDictData(MVS_APP_ENTITY_DICT, handle, tr);
                                if (o != null)
                                {
                                    ObjectId objId = HandleToObjectId(db, handle);
                                    if (objId.IsValid)
                                    {
                                        var dbobj = tr.GetObject(objId, OpenMode.ForWrite);
                                        if (dbobj != null && dbobj is Line line)
                                        {
                                            Point3d spt = line.StartPoint;
                                            Point3d ept = line.EndPoint;
                                            EntityUtils.SetLinePoints(objId, new Point3d(sx, sy, new_sz), new Point3d(tx, ty, new_tz), tr);
                                            count += 1;

                                            EditorUtils.Print(new ArrayList() {
                                                "\n[",
                                                count,
                                                "] 修改直线 -> ",
                                                "句柄:",
                                                handle,
                                                "\t原起点:",
                                                StringUtils.PointToString(spt),
                                                "\t原终点:",
                                                StringUtils.PointToString(ept),
                                                "\t新起点:",
                                                StringUtils.PointToString(new Point3d(sx, sy, new_sz)),
                                                "\t新终点:",
                                                StringUtils.PointToString(new Point3d(tx, ty, new_tz))
                                            });
                                            // 更新词典记录
                                            SaveLineToDict(handle, new Point3d(sx, sy, new_sz), new Point3d(tx, ty, new_tz), new_sz, new_tz, MVS_APP_POINT_DICT, MVS_APP_ENTITY_DICT, tr); ;
                                        }
                                        else
                                        {
                                            ignoredLineDatas.Add(lineData);
                                        }
                                    }
                                    else
                                    {
                                        ignoredLineDatas.Add(lineData);
                                    }
                                }
                                else
                                {
                                    ignoredLineDatas.Add(lineData);
                                }
                            }
                        }
                    }
                    catch (IOException ex)
                    {
                        EditorUtils.Print(new ArrayList() { "\n======= 读取文件失败:", ex.Message, "======" });
                    }
                }

                //EntityUtils.CreateLine(new Point3d(0, 0, 0), new Point3d(5000, 5000, 0), clayerName, tr);

                //tr.Commit();
                //return;

                // 继续处理ignoredLineDatas
                foreach (string lineData in ignoredLineDatas)
                {
                    // 拆分字符串
                    var dataList = StringUtils.StringSplit(lineData, "\t");
                    // 依次取出数据
                    string handle = dataList[0];
                    double sx = double.Parse(dataList[1]);
                    double sy = double.Parse(dataList[2]);
                    double sz = double.Parse(dataList[3]);
                    double tx = double.Parse(dataList[4]);
                    double ty = double.Parse(dataList[5]);
                    double tz = double.Parse(dataList[6]);
                    double new_sz = double.Parse(dataList[7]);
                    double new_tz = double.Parse(dataList[8]);

                    ObjectId objId = EntityUtils.CreateLine(new Point3d(sx, sy, new_sz), new Point3d(tx, ty, new_tz), clayerName, tr);
                    if (objId.IsValid)
                    {
                        count += 1;
                        //获取最新的数据, 更新词典记录
                        var dbObj = tr.GetObject(objId, OpenMode.ForRead);
                        string newHandle = HandleToHexString(dbObj.Handle);

                        EditorUtils.Print(new ArrayList() {
                                                "\n[",
                                                count,
                                                "] 绘制直线 -> ",
                                                "\t起点:",
                                                StringUtils.PointToString(new Point3d(sx, sy, new_sz)),
                                                "\t终点:",
                                                StringUtils.PointToString(new Point3d(tx, ty, new_tz)),
                                                "\t原句柄:",
                                                handle,
                                                "\t新句柄:",
                                                newHandle
                                            });
                        // 更新词典记录
                        SaveLineToDict(newHandle, new Point3d(sx, sy, new_sz), new Point3d(tx, ty, new_tz), new_sz, new_tz, MVS_APP_POINT_DICT, MVS_APP_ENTITY_DICT, tr); ;
                    }

                }

                //清理词典, 移除已被删除的直线
                CleanMvsAppDict(MVS_APP_POINT_DICT, MVS_APP_ENTITY_DICT, tr);

                // 提交事务
                tr.Commit();
            }
        }

        // 获取经纬网数据
        private static Tuple<bool, Point3d, double, double> GetGrateData(string msg)
        {
            var INVALD_RESULT = Tuple.Create<bool, Point3d, double, double>(false, Point3d.Origin, 0, 0);
            // 指定经纬参照点
            var ret1 = EditorUtils.GetPoint(msg);
            if (!ret1.Item1) return INVALD_RESULT;

            // 分别输入经纬值
            var ret2 = EditorUtils.GetDouble("\n请输入经度:");
            if (!ret2.Item1) return INVALD_RESULT;

            var ret3 = EditorUtils.GetDouble("\n请输入纬度:");
            if (!ret3.Item1) return INVALD_RESULT;

            return Tuple.Create<bool, Point3d, double, double>(true, ret1.Item2, ret2.Item2, ret3.Item2);
        }

        public enum InteractState
        {
            LINE,
            FIRST_POINT,
            SECOND_POINT,
            DONE
        };

        public struct SelectionResult
        {
            public bool Success;
            public ObjectId LineId;
            public Nullable<Point3d> StartPoint;
            public Nullable<Point3d> EndPoint;
        }

        public static SelectionResult SelectLineOrTwoPoints()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;

            var result = new SelectionResult();
            InteractState state = InteractState.LINE;

            while (state != InteractState.DONE)
            {
                switch (state)
                {
                    case InteractState.LINE:
                        // ISSUE Keywords.Add()无效，必须通过PromptEntityOptions构造函数指定关键词
                        // 注意: message参数字符串中必须要有[]!!!
                        var lineOpt = new PromptEntityOptions("\n选择直线 或 [两点(GP)]:", "GP");
                        //opt.SetRejectMessage("\n必须选择直线！");  // 自定义拒绝提示
                        //lineOpt.AddAllowedClass(typeof(Line), true);
                        //lineOpt.AllowNone = true;
                        //lineOpt.Keywords.Add("GP", "两点(GP)");
                        var lineRes = ed.GetEntity(lineOpt);

                        if (lineRes.Status == PromptStatus.Keyword && lineRes.StringResult == "GP")
                        {
                            state = InteractState.FIRST_POINT;
                        }
                        else if (lineRes.Status == PromptStatus.OK)
                        {
                            result.LineId = lineRes.ObjectId;
                            result.Success = true;
                            state = InteractState.DONE;
                        }
                        else
                        {
                            state = InteractState.DONE;
                        }
                        break;

                    case InteractState.FIRST_POINT:
                        // 注意: message参数字符串中必须要有[]!!!
                        var pt1Opt = new PromptPointOptions("\n选择起点 或 [直线(GL)]:", "GL");
                        //pt1Opt.Keywords.Add("GL", "直线(GL)");

                        var pt1Res = ed.GetPoint(pt1Opt);

                        if (pt1Res.Status == PromptStatus.OK)
                        {
                            result.StartPoint = pt1Res.Value;
                            state = InteractState.SECOND_POINT;
                        }
                        else if (pt1Res.Status == PromptStatus.Keyword)
                        {
                            state = InteractState.LINE;
                        }
                        else
                        {
                            state = InteractState.DONE;
                        }
                        break;

                    case InteractState.SECOND_POINT:
                        // 注意: message参数字符串中必须要有[]!!!
                        var pt2Opt = new PromptPointOptions("\n选择终点 或 [直线(GL)]:", "GL")
                        {
                            UseBasePoint = result.StartPoint.HasValue,
                            BasePoint = result.StartPoint ?? Point3d.Origin,
                            //AllowNone = true
                        };
                        //pt2Opt.Keywords.Add("GL", "直线(GL)");

                        var pt2Res = ed.GetPoint(pt2Opt);
                        if (pt2Res.Status == PromptStatus.OK)
                        {
                            result.EndPoint = pt2Res.Value;
                            result.Success = true;
                            state = InteractState.DONE;
                        }
                        else if (pt2Res.Status == PromptStatus.Keyword)
                        {
                            result.StartPoint = null;
                            state = InteractState.LINE;
                        }
                        else
                        {
                            state = InteractState.DONE;
                        }
                        break;
                }
            }
            return result;
        }

        // 选择直线，将xy坐标转经纬坐标
        [CommandMethod("line2grat")]
        public static void Line2Grat()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;
            Database db = doc.Database;

            var MVS_APP_GRAT_LAYER = "IMVS_APP_GRAT_LAYER";
            // 初始化操作
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                //图层颜色为黄色, 线宽为0.70mm
                LayerUtils.CreateLayer(MVS_APP_GRAT_LAYER, 2, LineWeight.LineWeight070, false, false, true, tr);
                tr.Commit();
            }

            // 记录命令执行过程中选定的2点以及计算得到的经纬数据、高亮用的直线id
            var gptDict = new Dictionary<string, Tuple<Point3d, Point3d, Point3d, Point3d, ObjectId>>();

            // 交互获取经纬度数据
            var ret1 = GetGrateData("\n请指定经纬网左下角:");
            if (!ret1.Item1) return;

            // 交互获取经纬度数据
            var ret2 = GetGrateData("\n请指定经纬网右上角:");
            if (!ret2.Item1) return;


            //var ret3 = EditorUtils.GetDouble("\n请指定当前水平的Z值:");
            //if (!ret3.Item1) return;

            Point3d lb_gpt = ret1.Item2; // 左下角
            double lb_bgx = ret1.Item3; // 经度
            double lb_bgy = ret1.Item4; // 纬度

            Point3d ru_gpt = ret2.Item2; // 右上角
            double ru_bgx = ret2.Item3; // 经度
            double ru_bgy = ret2.Item4; // 纬度

            double delta_bgx = (ru_bgx - lb_bgx) / (ru_gpt.X - lb_gpt.X);
            double delta_bgy = (ru_bgy - lb_bgy) / (ru_gpt.Y - lb_gpt.Y);
            EditorUtils.Print(new ArrayList() { "\n经度斜率:", delta_bgx, ", 纬度斜率:", delta_bgy, ", gdx=", ru_bgx - lb_bgx, ", dx=", ru_gpt.X - lb_gpt.X, ", gdy=", ru_bgy - lb_bgy, ", dy=", ru_gpt.Y - lb_gpt.Y });

            //double z = ret3.Item2; // z值
            double z = 0;


            using (Transaction tr = db.TransactionManager.StartTransaction())
            {

                var highlightLineIds = new ObjectIdCollection();

                int count = 0;
                EditorUtils.Print("\n=========== START:始末点坐标 -> 经纬度坐标 ============");
                while (true)
                {
                    var ret = SelectLineOrTwoPoints();
                    if (!ret.Success) break;

                    Nullable<Point3d> _spt = null;
                    Nullable<Point3d> _ept = null;
                    if (ret.LineId.IsValid)
                    {
                        DBObject o = tr.GetObject(ret.LineId, OpenMode.ForRead);
                        // 选择的是一条直线
                        if (o != null)
                        {
                            if (o is Line line)
                            {
                                _spt = line.StartPoint;
                                _ept = line.EndPoint;
                                //string handle = HandleToHexString(line.Handle);
                            }
                        }
                        else
                        {
                            EditorUtils.Print("\n[WARNING]选择的不是直线!");
                            continue;
                        }
                    }
                    else
                    {
                        _spt = ret.StartPoint;
                        _ept = ret.EndPoint;
                    }

                    if (_spt == null || _ept == null)
                    {
                        EditorUtils.Print("\n[ERROR]未知错误, LINE或POINT模式都无法获取两点坐标!");
                        break;
                    }


                    Point3d spt = _spt.Value;
                    Point3d ept = _ept.Value;

                    // 计算经纬坐标
                    Point3d g_spt = new Point3d((spt.X - lb_gpt.X) * delta_bgx + lb_bgx, (spt.Y - lb_gpt.Y) * delta_bgy + lb_bgy, z); // 经度、纬度、z值
                    Point3d g_ept = new Point3d((ept.X - lb_gpt.X) * delta_bgx + lb_bgx, (ept.Y - lb_gpt.Y) * delta_bgy + lb_bgy, z); // 经度、纬度、z值

                    // 记录到词典
                    string key = String.Format("{0}_{1}", StringUtils.PointToString(spt), StringUtils.PointToString(ept));
                    if (!gptDict.ContainsKey(key))
                    {
                        EditorUtils.Print(new ArrayList() {
                                            "\n[", count, "] ",
                                            "\t起点XY坐标: (", spt.X, ", ", spt.Y, ", ", spt.Z, ")",
                                            "\t起点经纬坐标: (", g_spt.X, ", ", g_spt.Y, ", ", g_spt.Z, ")",
                                            "\t终点XY坐标: (", ept.X, ", ", ept.Y, ", ", ept.Z, ")",
                                            "\t终点经纬坐标: (", g_ept.X, ", ", g_ept.Y, ", ", g_ept.Z, ")"
                                        });
                        // 新建临时直线，添加到高亮图层
                        Line line1 = new Line();
                        line1.StartPoint = spt;
                        line1.EndPoint = ept;
                        line1.Layer = MVS_APP_GRAT_LAYER;

                        // 记录到词典
                        gptDict.Add(key, Tuple.Create(spt, ept, g_spt, g_ept, EntityUtils.AppendToDb(line1, true, tr)));
                        // 计数器+1
                        count++;

                    }
                    else
                    {
                        EditorUtils.Print("\n这2点已经处理过了，跳过!");
                        continue;
                    }
                }
                EditorUtils.Print("\n=========== END:始末点坐标 -> 经纬度坐标 ============");
                // 提交事务
                tr.Commit();
            }

            if (gptDict.Count == 0) return;
            string filename = EditorUtils.GetFileForSave("mvs_grat.txt");
            if (filename != null)
            {
                // 追加模式打开文件
                using (StreamWriter writer = new StreamWriter(filename, true))
                {
                    // 写入当前时间(精确到秒)
                    writer.WriteLine("# 写入时间: " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));

                    // 写入到文件
                    foreach (string key in gptDict.Keys)
                    {
                        var data = gptDict[key];
                        Point3d spt = data.Item1;
                        Point3d ept = data.Item2;
                        Point3d g_spt = data.Item3;
                        Point3d g_ept = data.Item4;
                        MvsFileWriteLine(new List<double>() {
                            // 始末点几何坐标
                            spt.X, spt.Y, spt.Z, ept.X, ept.Y, ept.Z,
                            // 始末点经度、纬度、z值
                            g_spt.X, g_spt.Y, g_spt.Z, g_ept.X, g_ept.Y, g_ept.Z }.Cast<object>(),
                            writer, "\t");
                    }
                    EditorUtils.Print("\n======= 写入文件成功  ======");
                }
            }
            else
            {
                using (Transaction tr = db.TransactionManager.StartTransaction())
                {
                    // 写入到文件
                    foreach (string key in gptDict.Keys)
                    {
                        var data = gptDict[key];
                        //spt = data.Item1;
                        //Point3d ept = data.Item2;
                        //Point3d g_spt = data.Item3;
                        //Point3d g_ept = data.Item4;
                        ObjectId objId = data.Item5;
                        // 强制open锁定图层上的图形
                        var o = tr.GetObject(objId, OpenMode.ForWrite, false, true);
                        if (o != null && o is Entity ent)
                        {
                            ent.Erase();
                        }
                    }
                    tr.Commit();
                }
            }
        }

        // 经纬坐标转直线
        [CommandMethod("grat2line")]
        public static void Grat2Line()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;
            Database db = doc.Database;

            string filename = EditorUtils.GetFileForOpen("mvs_grat.txt");
            if (filename == null) return;

            // 交互获取经纬度数据
            var ret1 = GetGrateData("\n请指定经纬网左下角:");
            if (!ret1.Item1) return;

            // 交互获取经纬度数据
            var ret2 = GetGrateData("\n请指定经纬网右上角:");
            if (!ret2.Item1) return;

            Point3d lb_gpt = ret1.Item2; // 左下角
            double lb_bgx = ret1.Item3; // 经度
            double lb_bgy = ret1.Item4; // 纬度

            Point3d ru_gpt = ret2.Item2; // 右上角
            double ru_bgx = ret2.Item3; // 经度
            double ru_bgy = ret2.Item4; // 纬度

            double delta_bgx = (ru_bgx - lb_bgx) / (ru_gpt.X - lb_gpt.X);
            double delta_bgy = (ru_bgy - lb_bgy) / (ru_gpt.Y - lb_gpt.Y);
            EditorUtils.Print(new ArrayList() { "\n经度斜率:", delta_bgx, ", 纬度斜率:", delta_bgy, ", gdx=", ru_bgx - lb_bgx, ", dx=", ru_gpt.X - lb_gpt.X, ", gdy=", ru_bgy - lb_bgy, ", dy=", ru_gpt.Y - lb_gpt.Y });

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                using (StreamReader reader = new StreamReader(filename))
                {
                    while (!reader.EndOfStream)
                    {
                        string lineData = reader.ReadLine();
                        if (string.IsNullOrEmpty(lineData)) continue; // 跳过空行
                        if (lineData.StartsWith("#")) continue; // 跳过注释行

                        var datas = StringUtils.StringSplit(lineData, "\t");
                        // 提取7-12列经纬坐标数据
                        double g_spt_x = (double)StringUtils.StringToFloat(datas[6]);
                        double g_spt_y = (double)StringUtils.StringToFloat(datas[7]);
                        double g_spt_z = (double)StringUtils.StringToFloat(datas[8]);
                        double g_ept_x = (double)StringUtils.StringToFloat(datas[9]);
                        double g_ept_y = (double)StringUtils.StringToFloat(datas[10]);
                        double g_ept_z = (double)StringUtils.StringToFloat(datas[11]);

                        // 绘制直线
                        //ObjectId objId = 
                        EntityUtils.CreateLine(
                        new Point3d((g_spt_x - lb_bgx) / delta_bgx + lb_gpt.X, (g_spt_y - lb_bgy) / delta_bgy + lb_gpt.Y, g_spt_z), // 经纬坐标转换为几何坐标
                        new Point3d((g_ept_x - lb_bgx) / delta_bgx + lb_gpt.X, (g_ept_y - lb_bgy) / delta_bgy + lb_gpt.Y, g_ept_z), // 经纬坐标转换为几何坐标
                        null, tr);
                    }
                }
                // 提交事务
                tr.Commit();
            }
        }

        //class TwoPoint3dObject
        //{
        //    public Point3d Spt { set; get; }
        //    public Point3d Ept { set; get; }

        //    public override bool Equals(object obj)
        //    {
        //        if (obj == null || GetType() != obj.GetType())
        //        {
        //            return false;
        //        }
        //        TwoPoint3dObject other = (TwoPoint3dObject)obj;
        //        return Spt.Equals(other.Spt) && Ept.Equals(other.Ept);
        //    }

        //    public override int GetHashCode()
        //    {
        //        // 使用 XOR 操作符来组合 Spt 和 Ept 的哈希码
        //        return Spt.GetHashCode() ^ Ept.GetHashCode(); // XOR 操作符确保了即使两个属性的哈希码相同，组合后的哈希码也不会相同（除非两者都相同）
        //    }
        //}        
    }
}